package com.autumn.sms.service.utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;

import com.autumn.util.ExceptionUtils;

/**
 * 封装关于Http请求的业务功能
 * 
 * @author JuWa ▪ Zhang
 * @date 2018年1月9日
 */
@SuppressWarnings("deprecation")
public class HttpUtil {

	private static String userAgent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36";

	private static int TIMEOUT = 1000 * 30;

	/**
	 * Post参数到接口，返回接受接口响应的返回值
	 * 
	 * @param data
	 * @param serverUrl
	 * @return
	 *
	 */
	public static String postParam(Map<String, Object> data, String serverUrl) {
		String urlencode = urlencode(data);
		StringBuffer sb = new StringBuffer();
		PrintWriter pw = null;
		BufferedReader br = null;
		HttpURLConnection con = null;
		try {
			URLEncoder.encode(urlencode, charset.name());
			URL url = new URL(serverUrl);
			con = (HttpURLConnection) url.openConnection();
			con.setRequestProperty("User-agent", userAgent);
			con.setDoOutput(true);
			con.setUseCaches(false);
			con.setDoInput(true);
			con.setConnectTimeout(TIMEOUT);
			con.setReadTimeout(TIMEOUT);
			con.setRequestMethod("GET");
			pw = new PrintWriter(new OutputStreamWriter(con.getOutputStream(), charset), true);
			pw.write(urlencode, 0, urlencode.length());
			pw.flush();
			br = new BufferedReader(new InputStreamReader(con.getInputStream(), charset));
			String str = null;
			while ((str = br.readLine()) != null) {
				sb.append(str);
			}
			return sb.toString();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (pw != null) {
				pw.close();
			}
			if (br != null) {
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (con != null) {
				con.disconnect();
			}
		}
		return null;
	}

	protected static String urlencode(Map<String, Object> data) {
		StringBuilder sb = new StringBuilder();
		int n = 0;
		for (Map.Entry<String, Object> i : data.entrySet()) {
			if (n > 0) {
				sb.append("&");
			}
			sb.append(i.getKey()).append("=").append(i.getValue());
			n++;
		}
		return sb.toString();
	}

	/**
	 * get请求
	 * 
	 * @param host
	 * @param path
	 * @param method
	 * @param headers
	 * @param querys
	 * @return
	 * @throws Exception
	 */
	public static HttpResponse doGet(String host, Map<String, String> headers, Map<String, String> querys)
			throws Exception {
		// 若host是https://，绕过SSL双向认证的封装方法
		HttpClient httpClient = wrapClient(host);
		HttpGet request = new HttpGet(buildUrl(host, querys));
		for (Map.Entry<String, String> e : headers.entrySet()) {
			request.addHeader(e.getKey(), e.getValue());
		}

		return httpClient.execute(request);
	}

	private static Charset charset = Charset.forName("UTF-8");

	/**
	 * post form
	 * 
	 * @param host
	 * @param path
	 * @param method
	 * @param headers
	 * @param querys
	 * @param bodys
	 * @return
	 * @throws Exception
	 */
	public static HttpResponse doPost(String host, String method, Map<String, String> headers,
			Map<String, String> querys, Map<String, String> bodys) throws Exception {

		HttpClient httpClient = wrapClient(host);
		HttpPost request = new HttpPost(buildUrl(host, querys));
		for (Map.Entry<String, String> e : headers.entrySet()) {
			request.addHeader(e.getKey(), e.getValue());
		}

		if (bodys != null) {
			List<NameValuePair> nameValuePairList = new ArrayList<NameValuePair>();

			for (String key : bodys.keySet()) {
				nameValuePairList.add(new BasicNameValuePair(key, bodys.get(key)));
			}
			UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nameValuePairList, charset);
			formEntity.setContentType("application/x-www-form-urlencoded; charset=UTF-8");
			request.setHeader("User-Agent", userAgent);
			request.setEntity(formEntity);
		}

		return httpClient.execute(request);
	}

	/**
	 * Post String
	 * 
	 * @param host
	 * @param path
	 * @param method
	 * @param headers
	 * @param querys
	 * @param body
	 * @return
	 * @throws Exception
	 */
	public static HttpResponse doPost(String host, String method, Map<String, String> headers,
			Map<String, String> querys, String body) throws Exception {

		HttpClient httpClient = wrapClient(host);
		HttpPost request = new HttpPost(buildUrl(host, querys));
		for (Map.Entry<String, String> e : headers.entrySet()) {
			request.addHeader(e.getKey(), e.getValue());
		}

		if (StringUtils.isNotBlank(body)) {
			request.setEntity(new StringEntity(body, charset));
		}
		return httpClient.execute(request);
	}

	/**
	 * Post stream
	 * 
	 * @param host
	 * @param path
	 * @param method
	 * @param headers
	 * @param querys
	 * @param body
	 * @return
	 * @throws Exception
	 */
	public static HttpResponse doPost(String host, String method, Map<String, String> headers,
			Map<String, String> querys, byte[] body) throws Exception {
		HttpClient httpClient = wrapClient(host);

		HttpPost request = new HttpPost(buildUrl(host, querys));
		for (Map.Entry<String, String> e : headers.entrySet()) {
			request.addHeader(e.getKey(), e.getValue());
		}

		if (body != null) {
			request.setEntity(new ByteArrayEntity(body));
		}

		return httpClient.execute(request);
	}

	private final static String HTTPS_BEGIN = "https://";

	/**
	 * 通用的绕过SSL双向认证的封装方法
	 * 
	 * @param host
	 * @return
	 */
	private static HttpClient wrapClient(String host) {
		// HttpClient httpClient = new DefaultHttpClient();
		HttpClient httpClient = HttpClientBuilder.create().build();
		if (host.startsWith(HTTPS_BEGIN)) {
			sslClient(httpClient);
		}
		return httpClient;
	}

	/**
	 * 基于SSL协议的http请求对象:https通信相对于http通信多了一层 客户端与服务端之间的双向认证 这里是绕过SSL双向认证的封装方法
	 * 
	 * @param cli
	 */
	private static void sslClient(HttpClient httpClient) {
		try {
			SSLContext ctx = SSLContext.getInstance("TLS");
			// x509证书信任管理
			TrustManager tm = new X509TrustManager() {
				@Override
				public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {

				}

				@Override
				public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {

				}

				@Override
				public X509Certificate[] getAcceptedIssuers() {
					return null;
				}
			};
			ctx.init(null, new TrustManager[] { tm }, new SecureRandom());
			SSLSocketFactory ssf = new SSLSocketFactory(ctx);
			ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

			ClientConnectionManager ccm = httpClient.getConnectionManager();
			SchemeRegistry registry = ccm.getSchemeRegistry();
			registry.register(new Scheme("https", 443, ssf));

		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (KeyManagementException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 把请求消息拼接到url后面一起提交
	 * 
	 * @param host
	 * @param path
	 * @param querys
	 * @return
	 * @throws ServiceException
	 */
	private static String buildUrl(String host, Map<String, String> querys) {
		StringBuilder sb = new StringBuilder();
		ExceptionUtils.checkNotNullOrBlank(host, "host");
		sb.append(host);
		if (querys != null) {
			StringBuilder sub = new StringBuilder();
			for (Entry<String, String> entry : querys.entrySet()) {
				if (sub.length() > 0) {
					sub.append("&");
				}
				if (entry.getKey() == null && StringUtils.isNotBlank(entry.getValue())) {
					sub.append(entry.getValue());
					continue;
				}
				if (StringUtils.isNotBlank(entry.getKey())) {
					sub.append(entry.getKey());
					if (StringUtils.isNotBlank(entry.getValue())) {
						try {
							sub.append("=" + URLEncoder.encode(entry.getValue(), charset.name()));
							continue;
						} catch (UnsupportedEncodingException e) {
							e.printStackTrace();
						}
					}
				}
			}
			sb.append("?" + sub);
		}
		return sb.toString();
	}
}
